package cn.hh.user.service.impl;


import cn.hh.basic.constant.UserConstants;
import cn.hh.basic.execption.BusinessException;
import cn.hh.basic.service.impl.BasicServiceImpl;
import cn.hh.basic.util.AjaxResult;
import cn.hh.basic.util.HttpClientUtils;
import cn.hh.basic.util.MD5Utils;
import cn.hh.basic.util.StrUtils;
import cn.hh.user.constant.WechatConstants;
import cn.hh.user.domain.LoginInfo;
import cn.hh.user.domain.User;
import cn.hh.user.domain.WxUser;
import cn.hh.user.dto.LoginLo;
import cn.hh.user.dto.UserLo;
import cn.hh.user.mapper.LoginInfoMapper;
import cn.hh.user.mapper.UserMapper;
import cn.hh.user.mapper.WxUserMapper;
import cn.hh.user.service.ILoginInfoService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.mockito.internal.configuration.MockAnnotationProcessor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LoginInfoServiceImpl extends BasicServiceImpl<LoginInfo> implements ILoginInfoService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private LoginInfoMapper loginInfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private WxUserMapper wxUserMapper;

    /*用户注册时添加信息至用户信息表*/
    @Override
    @Transactional
    public void register(UserLo userLo) {
        // 1.验证数据是否存在
        if(StringUtils.isEmpty(userLo.getCode())
                ||StringUtils.isEmpty(userLo.getPhone())
                ||StringUtils.isEmpty(userLo.getPassword())
                ||StringUtils.isEmpty(userLo.getPasswordRepeat())){
            /*如果其中有一个为空，则抛出异常：HXD，请完善注册信息*/
            throw new BusinessException("HXD，请完善注册信息!!!!!!");
        }
        // 2.验证输入的两次密码是否一致
        if(!userLo.getPassword().equals(userLo.getPasswordRepeat())){
            /*如果两次输入的密码不一致，则抛出异常：HXD，密码输入不一致，请仔细检查后重新输入*/
            throw new BusinessException("HXD，密码输入不一致，请仔细检查后重新输入!!!!!!");
        }
        // 3.从redis取出验证码校对验证码是否一致
        String phone = userLo.getPhone();
        /*查询redis中的验证码*/
        Object codeLo = redisTemplate.opsForValue().get(UserConstants.USER_REGISTER_CODE_PREFIX+phone);
        // 3.1验证码过期，从redis中获取不到
        String code = (String)codeLo;
        System.out.println("从redis获取的验证码"+code);
        if (code==null){
            /*获取不到数据则表明验证码已过期，抛出异常：HXD，验证码失效请重新获取！！！*/
            throw new BusinessException("HXD，验证码失效请重新获取!!!!!!");
        }else {
            // 3.2验证码未过期，校对是否一致
            /*获取到redis中的验证码rcode*/
            String rcode = code.split(":")[0];
            System.out.println(rcode);
            /*对比redis中验证码和传入进来的验证码是否一致*/
            if(!rcode.equals(userLo.getCode())){
                /*传入进来的验证码和从redis中取出的验证码不一致，抛出异常：HXD，请输入正确的验证码！！！*/
                throw new BusinessException("HXD，请输入正确的验证码!!!!!!");
            }
        }
        // 4.将UserLo转为User
        User user = new User();
        /*使用UUID添加盐值*/
        String salt = UUID.randomUUID().toString();
        System.out.println("盐值++++"+salt);
        user.setPhone(userLo.getPhone());
        user.setSalt(salt);
        /*设置密码时要加盐加密*/
        String password = MD5Utils.encrypByMd5(userLo.getPassword()+salt);
        user.setPassword(password);
        /*手机号注册，直接启用*/
        user.setState(1);
        // 5.将User转为LoginInfo
        LoginInfo loginInfo = new LoginInfo();
        /*使用copy方法,将第一个的数据拷贝到第二个目标*/
        BeanUtils.copyProperties(user, loginInfo);
        /*设置不相同的属性*/
        /*由于是前台页面注册的用户信息，属性设置为1，标明是前台用户*/
        loginInfo.setType(1);
        /*启用禁用随user中的启用禁用*/
        loginInfo.setDisable(user.getState());
        // 6.存储LoginInfo
        loginInfoMapper.add(loginInfo);
        // 7.关联LoginInfo_id
        /*由于xml中写了返回主键，现在可以直接获取logininfo的id*/
        user.setLogininfo_id(loginInfo.getId());
        // 8.存储User
        userMapper.add(user);
    }
    /*用户登录*/
    @Override
    public Map<String,Object> login(LoginLo loginLo) {
        // 1.验证是否为空
        if(StringUtils.isEmpty(loginLo.getPassword())||StringUtils.isEmpty(loginLo.getUsername())||StringUtils.isEmpty(loginLo.getType())){
            /*有一个为空就抛出异常：HXD，请输入账号密码！！！*/
            throw new BusinessException("HXD，请填写登录信息!!!!!!");
        }
        // 2.验证用户密码在数据库中是否存在
        /*获取传入的username去数据库进行判定，要判定三个数据，username，phone，email*/
        LoginInfo loginInfo = loginInfoMapper.loadByUsername(loginLo);
        /*判断获取到的数据是否为空*/
        if (loginInfo==null){
            /*数据为空抛出异常：该用户登录信息不存在，需要注册*/
            throw new BusinessException("HXD，该用户登录信息不存在，请前往注册!!!!!!");
        }
        // 3.校验登录信息
        /*取出盐值进行加密加盐*/
        String md5Pwd = MD5Utils.encrypByMd5(loginLo.getPassword()+loginInfo.getSalt());
        // 3.1校验密码是否一致
        if (!md5Pwd.equals(loginInfo.getPassword())){
            /*数据为空抛出异常：用户名或密码错误*/
            throw new BusinessException("HXD，用户名或密码错误!!!!!!");
        }
        // 3.2判断账户是否被封禁
        if(loginInfo.getDisable()!=1){
            /*数据为空抛出异常：该账户已被封禁*/
            throw new BusinessException("HXD，该账户已被封禁!!!!!!");
        }
        // 4.登陆成功
        /*生成token*/
        String token = UUID.randomUUID().toString();
        /*存入redis：唯一标识，用户登录信息，失效30，单位分钟*/
        redisTemplate.opsForValue().set(token,loginInfo ,30 , TimeUnit.MINUTES );
        // 4.保存登录信息到session中
        /*返回token loginInfo信息*/
        Map<String,Object> map = new HashMap<>();
        map.put("token", token);
        /*为了安全，将盐值和密码清空*/
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
        /*返回logininfo信息*/
        map.put("loginInfo", loginInfo);
        System.out.println("感觉怎么样"+loginLo.toString());
        return map;
    }
    /*微信登陆*/
    @Override
    public AjaxResult wechatLogin(String code) {
        System.out.println("++++++微信登录实现层+++++");
        // 1.通过code去拿token OAuth第二步
        String getTokenUrl = WechatConstants.GET_TOKEN_URL
                .replace("APPID", WechatConstants.APPID)
                .replace("SECRET", WechatConstants.SECRET)
                .replace("CODE", code);
        String tokenStr = HttpClientUtils.httpGet(getTokenUrl);
        System.out.println("+++++tokenStr++++++"+tokenStr);
        if (StringUtils.isEmpty(tokenStr)){
            throw  new BusinessException("微信炸了，稍后再来");
        }
        // 2.获取到token，自带了openid(微信号)
        JSONObject tokenObj = JSONObject.parseObject(tokenStr);
        String accessToken = tokenObj.getString("access_token");
        String openid = tokenObj.getString("openid");
        // 3.查询t_wxuser，看看有没有注册过
        WxUser wxUser = wxUserMapper.loadByOpenId(openid);
        if (wxUser!=null && wxUser.getUser_id()!=null){
            System.out.println("++++++微信登录成功后判定+++++");
            // 3.1如果有且绑定了user_id，直接免密登录
            // 4.登陆成功
            LoginInfo loginInfo = loginInfoMapper.loadByUserId(wxUser.getUser_id());
            /*生成token*/
            String token = UUID.randomUUID().toString();
            /*存入redis：唯一标识，用户登录信息，失效30，单位分钟*/
            redisTemplate.opsForValue().set(token,loginInfo ,30 , TimeUnit.MINUTES );
            // 4.保存登录信息到session中
            /*返回token loginInfo信息*/
            Map<String,Object> map = new HashMap<>();
            map.put("token", token);
            /*为了安全，将盐值和密码清空*/
            loginInfo.setSalt(null);
            loginInfo.setPassword(null);
            /*返回logininfo信息*/
            map.put("loginInfo", loginInfo);
            return AjaxResult.me().setResultObj(map);
        }else {
            // 3.2如果没有，封装token和openid返回到前端，并跳转到前端绑定页面
            String returnUrlParam = "accessToken="+accessToken+"&openId="+openid;
            System.out.println("++++++微信登录失败+++++");
            System.out.println("后端返回的值"+returnUrlParam);
            System.out.println("返回前台的判定值"+AjaxResult.me().setSuccess(false).getSuccess());
            return AjaxResult.me().setSuccess(false).setResultObj(returnUrlParam);
        }
    }
    /*微信绑定*/
    @Override
    public Map<String,Object> wechatBinder(Map<String, String> param) {
        // 1.空校验
        /*后面需要使用到信息，所以直接取出来再校验*/
        String phone=param.get("phone");
        String verifyCode=param.get("verifyCode");
        String accessToken=param.get("accessToken");
        String openId=param.get("openId");
        System.out.println(phone+"++"+verifyCode+"++"+accessToken+"++"+openId);
        if (StringUtils.isEmpty(phone)||StringUtils.isEmpty(verifyCode)||
                StringUtils.isEmpty(accessToken)||StringUtils.isEmpty(openId)){
            /*只要有一个为空就抛出错误：HXD；请输入正确信息*/
            throw new BusinessException("HXD；请输入正确绑定信息！！！！");
        }
        // 2.验证码校验
        /*通过手机号获取redis中的验证码*/
        Object redisCode = redisTemplate.opsForValue().get(UserConstants.USER_REGISTER_CODE_PREFIX + phone);
        // 2.1验证码是否有
        if (redisCode==null){
            /*没有验证码代表验证码过期抛出错误：HXD；验证码已失效请重新获取*/
            throw new BusinessException("HXD；请输入正确绑定信息！！！！");
        }
        // 2.2验证码是否一致
        /*验证码存在，获取验证码对比是否一致*/
        String code = ((String)redisCode).split(":")[0];
        if (!code.equals(verifyCode)){
            /*不一致就抛出错误：HXD；验证码错误！！！*/
            throw new BusinessException("HXD；验证码错误！！！！");
        }
        // 2.3查询user表是否有注册过
        /*通过电话号码查询是否注册*/
        User user = userMapper.loadByPhone(phone);
        System.out.println("通过电话获取到的"+user);
        // 2.3.1未注册过则将信息同步至t_user和t_logininfo
        /*声明LoginInfo类型变量后面使用*/
        LoginInfo loginInfo = new LoginInfo();
        if (user==null){
            /*保存信息至t_user和t_logininfo*/
            user=wxPhone2User(phone);
            System.out.println("++++++微信绑定设置user+++++"+user);
            BeanUtils.copyProperties(user,loginInfo);
            System.out.println("++++++++++++++++++++++++++");
            loginInfo.setType(1);
            System.out.println("++++++loginInfo绑定前+++++"+loginInfo);
            loginInfoMapper.add(loginInfo);
            System.out.println("++++++loginInfo绑定后+++++");
            user.setLogininfo_id(loginInfo.getId());
            userMapper.add(user);
            System.out.println("++++++微信绑定后+++++");
        }else {
            // 2.3.2根据该账号查询是否有微信绑定
            /*为了避免出现一个用户多个微信绑定情况，扩展业务还要查询该信息是否被绑定微信*/
            WxUser wxUser = wxUserMapper.loadByUserId(user.getId());
            if (wxUser!=null){
                /*能查到绑定的微信用户就抛出错误：HXD；该账号已绑定微信账号！！！*/
                throw new BusinessException("HXD；该账号已绑定微信账号！！！！");
            }else {
                // 2.3.3查询出该账号信息
                System.out.println("user里得id"+user.getLogininfo_id());
                loginInfo = loginInfoMapper.loadLoginInfoByUserId(user.getLogininfo_id());
                System.out.println("获取的数据"+loginInfo);
            }
        }
        /*使用token等信息到微信获取权限查询数据*/
        String getURL=WechatConstants.GET_WXUSER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId);
        /*发送http的get请求，返回所需要的数据*/
        String wxUserStr = HttpClientUtils.httpGet(getURL);
        WxUser wxUser = user2WxUser(wxUserStr,user);
        /*绑定成功*/
        wxUserMapper.add(wxUser);
        // 2.4绑定成功登录
        /*免密登录需要一个验证码，使用uuid生成的token*/
        String token = UUID.randomUUID().toString();
        // 3.储存redis
        redisTemplate.opsForValue().set(token,
                loginInfo,
                30,
                TimeUnit.MINUTES);
        /*返回token和logininfo信息*/
        Map<String,Object> map = new HashMap<>();
        map.put("token", token);
        /*在保存logininfo信息前将重要信息隐藏（清空处理）*/
        System.out.println("绑定的数据"+loginInfo);
        loginInfo.setPassword(null);
        loginInfo.setSalt(null);
        map.put("loginInfo", loginInfo);
        return map;
    }

    private WxUser user2WxUser(String wxUserStr, User user) {
        /*转换微信信息*/
        JSONObject jsonObject = JSON.parseObject(wxUserStr);
        WxUser wxUser = new WxUser();
        wxUser.setUser_id(user.getId());
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setAddress(jsonObject.getString("address"));
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        return wxUser;
    }

    private User wxPhone2User(String phone) {
        User user = new User();
        /*将传入进来的电话号码设置为用户名*/
        user.setPhone(phone);
        user.setUsername(phone);
        /*自动生成一个随机的六位数密码，后续以短信形式发送到用户*/
        String randomString = StrUtils.getRandomString(6);
        System.out.println("应该发送给客户的初始密码++++++++"+randomString);
        /*生成盐值*/
        String salt = StrUtils.getComplexRandomString(32);
        user.setSalt(salt);
        /*设置密码，加密加盐*/
        user.setPassword(MD5Utils.encrypByMd5(randomString+salt));
        /*设置状态，手机号注册，直接设置为启用状态，1*/
        user.setState(1);
        return user;
    }
}
